From a0dec4e1b0688da7c65b05543710351b41d39303 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 14 Jul 2014 10:15:53 -0700 Subject: [PATCH] Allow tables in configuration --- src/cargo/ops/cargo_compile.rs | 4 +- src/cargo/util/config.rs | 193 +++++++++++++++++---------------- 2 files changed, 102 insertions(+), 95 deletions(-) diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs index 18b0c0587..44fba5c80 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -113,7 +113,9 @@ fn source_ids_from_config() -> CargoResult> { let paths: Vec = match *config_paths.get_value() { config::String(_) => return Err(internal("The path was configured as \ - a String instead of a List")), + a String instead of a List")), + config::Table(_) => return Err(internal("The path was configured as \ + a Table instead of a List")), config::List(ref list) => { list.iter().map(|path| Path::new(path.as_slice())).collect() } diff --git a/src/cargo/util/config.rs b/src/cargo/util/config.rs index 1745ae1ce..7525e7787 100644 --- a/src/cargo/util/config.rs +++ b/src/cargo/util/config.rs @@ -1,4 +1,4 @@ -use std::{io,fmt,os, result}; +use std::{io, fmt, os, result, mem}; use std::collections::HashMap; use serialize::{Encodable,Encoder}; use toml; @@ -69,30 +69,27 @@ pub enum Location { #[deriving(Eq,PartialEq,Clone,Decodable)] pub enum ConfigValueValue { String(String), - List(Vec) + List(Vec), + Table(HashMap), } impl fmt::Show for ConfigValueValue { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - &String(ref string) => write!(f, "{}", string), - &List(ref list) => write!(f, "{}", list) + match *self { + String(ref string) => write!(f, "{}", string), + List(ref list) => write!(f, "{}", list), + Table(ref table) => write!(f, "{}", table), } } } impl> Encodable for ConfigValueValue { fn encode(&self, s: &mut S) -> Result<(), E> { - match self { - &String(ref string) => { - raw_try!(string.encode(s)); - }, - &List(ref list) => { - raw_try!(list.encode(s)); - } + match *self { + String(ref string) => string.encode(s), + List(ref list) => list.encode(s), + Table(ref table) => table.encode(s), } - - Ok(()) } } @@ -110,6 +107,70 @@ impl ConfigValue { pub fn get_value<'a>(&'a self) -> &'a ConfigValueValue { &self.value } + + fn from_toml(path: &Path, toml: toml::Value) -> CargoResult { + let value = match toml { + toml::String(val) => String(val), + toml::Array(val) => { + List(try!(result::collect(val.move_iter().map(|toml| { + match toml { + toml::String(val) => Ok(val), + _ => Err(internal("")), + } + })))) + } + toml::Table(val) => { + Table(try!(result::collect(val.move_iter().map(|(key, value)| { + let value = raw_try!(ConfigValue::from_toml(path, value)); + Ok((key, value)) + })))) + } + _ => return Err(internal("")) + }; + + Ok(ConfigValue { value: value, path: vec![path.clone()] }) + } + + fn merge(&mut self, from: ConfigValue) -> CargoResult<()> { + let ConfigValue { value, path } = from; + match (&mut self.value, value) { + (&String(ref mut old), String(ref mut new)) => { + mem::swap(old, new); + self.path = path; + } + (&List(ref mut old), List(ref mut new)) => { + old.extend(mem::replace(new, Vec::new()).move_iter()); + self.path.extend(path.move_iter()); + } + (&Table(ref mut old), Table(ref mut new)) => { + let new = mem::replace(new, HashMap::new()); + for (key, value) in new.move_iter() { + let mut err = Ok(()); + old.find_with_or_insert_with(key, value, + |_, old, new| err = old.merge(new), + |_, new| new); + try!(err); + } + self.path.extend(path.move_iter()); + } + (expected, found) => { + return Err(internal(format!("expected {}, but found {}", + expected.desc(), found.desc()))) + } + } + + Ok(()) + } +} + +impl ConfigValueValue { + fn desc(&self) -> &'static str { + match *self { + Table(..) => "table", + List(..) => "array", + String(..) => "string", + } + } } impl> Encodable for ConfigValue { @@ -137,13 +198,27 @@ pub fn get_config(pwd: Path, key: &str) -> CargoResult { } pub fn all_configs(pwd: Path) -> CargoResult> { - let mut map = HashMap::new(); - - try!(walk_tree(&pwd, |file| extract_all_configs(file, &mut map)).map_err(|_| - human("Couldn't load Cargo configuration"))); + let mut cfg = ConfigValue { value: Table(HashMap::new()), path: Vec::new() }; + + try!(walk_tree(&pwd, |mut file| { + let path = file.path().clone(); + let contents = try!(file.read_to_string()); + let file = path.filename_display().to_string(); + let table = try!(cargo_toml::parse(contents.as_slice(), + file.as_slice()).chain_error(|| { + internal(format!("could not parse Toml manifest; path={}", + path.display())) + })); + let value = try!(ConfigValue::from_toml(&path, toml::Table(table))); + try!(cfg.merge(value)); + Ok(()) + }).map_err(|_| human("Couldn't load Cargo configuration"))); - Ok(map) + match cfg.value { + Table(map) => Ok(map), + _ => unreachable!(), + } } fn find_in_tree(pwd: &Path, @@ -192,80 +267,10 @@ fn walk_tree(pwd: &Path, fn extract_config(mut file: io::fs::File, key: &str) -> CargoResult { let contents = try!(file.read_to_string()); - let toml = try!(cargo_toml::parse(contents.as_slice(), - file.path().filename_display() - .to_string().as_slice())); - let val = try!(toml.find_equiv(&key).require(|| internal(""))); - - let v = match *val { - toml::String(ref val) => String(val.clone()), - toml::Array(ref val) => { - List(val.iter().map(|s: &toml::Value| s.to_string()).collect()) - } - _ => return Err(internal("")) - }; - - Ok(ConfigValue{ value: v, path: vec![file.path().clone()] }) -} - -fn extract_all_configs(mut file: io::fs::File, - map: &mut HashMap) -> CargoResult<()> { - let path = file.path().clone(); - let contents = try!(file.read_to_string()); - let file = path.filename_display().to_string(); - let table = try!(cargo_toml::parse(contents.as_slice(), - file.as_slice()).chain_error(|| { - internal(format!("could not parse Toml manifest; path={}", - path.display())) - })); - - for (key, value) in table.iter() { - match value { - &toml::String(ref val) => { - map.insert(key.clone(), ConfigValue { - value: String(val.clone()), - path: vec!(path.clone()) - }); - } - &toml::Array(ref val) => { - let config = map.find_or_insert_with(key.clone(), |_| { - ConfigValue { path: vec!(), value: List(vec!()) } - }); - - try!(merge_array(config, val.as_slice(), - &path).chain_error(|| { - internal(format!("The `{}` key in your config", key)) - })); - }, - _ => () - } - } + let mut toml = try!(cargo_toml::parse(contents.as_slice(), + file.path().filename_display() + .to_string().as_slice())); + let val = try!(toml.pop_equiv(&key).require(|| internal(""))); - Ok(()) -} - -fn merge_array(existing: &mut ConfigValue, val: &[toml::Value], - path: &Path) -> CargoResult<()> { - match existing.value { - String(_) => Err(internal("should be an Array, but it was a String")), - List(ref mut list) => { - let r: CargoResult> = result::collect(val.iter().map(toml_string)); - match r { - Err(_) => Err(internal("should be an Array of Strings, but \ - was an Array of other values")), - Ok(new_list) => { - list.push_all(new_list.as_slice()); - existing.path.push(path.clone()); - Ok(()) - } - } - } - } -} - -fn toml_string(val: &toml::Value) -> CargoResult { - match val { - &toml::String(ref str) => Ok(str.clone()), - _ => Err(internal("")) - } + ConfigValue::from_toml(file.path(), val) } -- 2.30.2